先来搞明白基础配置
目标
我们目标是配置一个使用Html、JavaScript、CSS来完成基础的开发,能够处理js文件、处理css和一些静态文件,我们一步步来~
基础配置实践
基础路径:
1 2 3 4 5 6 7 8
| ├── node_modules ├── package.json ├── src │ ├── index.html │ ├── index.js │ ├── test.png │ └── index.css └── webpack.config.js
|
依赖版本:
1 2 3 4 5 6 7 8 9 10 11 12
| { "devDependencies": { "css-loader": "^1.0.0", "file-loader": "^1.1.11", "html-webpack-plugin": "^3.2.0", "mini-css-extract-plugin": "^0.4.1", "style-loader": "^0.21.0", "url-loader": "^1.0.1", "webpack": "^4.16.1", "webpack-cli": "^3.1.0" } }
|
初始化project
第一步是初始化project,创建一个文件夹webpack-study,键入npm init,一路enter,最终文件夹下生成了一个package.json文件.
安装依赖包
第二步是安装webpack与webpack-cli,为什么还要安装webpack-cli,因为webpack的指令需要webpack-cli来实现,两者目前来说是不可分离的,所以:
npm i webpack webpck-cli -D
编辑config文件
然后编辑webpack.config.js文件:
1 2 3 4 5 6 7 8 9 10 11
| const path = require('path')
module.exports = (env, argv) => { return { entry: './src/index.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'js/bundle.js' }, } }
|
这是最基础的配置,我们主要定义的是如何去处理js文件,它的入口文件在./src/index.js,打包后文件存储在dist目录下,js文件存储在dist文件夹中的js/中,命名为bundle.js.
编写代码
我们试试编辑index.html和index.js:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <h2>this is a webpack test</h2> </body> <script src="./index.js"></script> </html>
|
1 2
| document.write('xixixi')
|
然后,在package.json中的scripts添加npm指令:
1 2 3 4 5 6 7 8
| { ... "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack --mode production" } ... }
|
运行打包
运行npm run build后,会得出一个dist文件夹,如下:
1 2 3 4 5 6 7 8
| ├── dist │ └── js │ └── bundle.js ├── package.json ├── src │ ├── index.html │ └── index.js └── webpack.config.js
|
这样我们就得到了打包结果,我们写的index.js被打包到dist中,但是这并不是一个完整的结构,因为dist文件中并没有html文件,所以我们进一步优化:
添加html-webpack-plugin插件
npm i html-webpack-plugin -D
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| const path = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = (env, argv) => { return { entry: './src/index.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'js/bundle.js' }, plugins: [ new HtmlWebpackPlugin({ template: './src/index.html', filename: 'index.html' }) ] } }
|
html-webpack-plugin功能非常强大,template是我们指定的模版文件,filename是我们打包后的html文件名,最为神奇的是html-webpack-plugin会自动将打包后的文件导入到index.html中。
处理CSS
我们下一个问题是如何打包css文件,因为webpack只能处理js文件,那么就需要对应的loader来处理css文件:
npm i css-loader style-loader -D
- css-loader: 解析.css文件
- style-loader: 将解析完成的内容插入到js中,通过js将样式绑定到dom元素中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| module.exports = (env, argv) => { return { ... module: { rules: [ { test: /\.css/, use: [ 'style-loader', 'css-loader' ] } ] } } }
|
这里注意是是loader的使用顺序是从右到左的,上面的配置即表示先使用css-loader解析css文件,在用style-loader将样式绑定到dom元素中.
新增index.css文件:
修改index.js,将css导入:
1 2
| require('./index.css') document.write('xixixi')
|
我们运行npm run build,发现html中有样式,但是css却是嵌入到js中的,这很不优雅,我们都希望各类文件排排坐,分离开来.
分离CSS
为了各司其职,我们决定将css分离开来,这次我们使用mini-css-extract-plugin这个插件来替代style-loader,为什么说他们两是替代关系呢?因为我们之前说style-loader是将css样式插入到dom元素中,那么如果要分离css,那么就不能使用style-loader,mini-css-extract-plugin就是将css文件单独分开来:
npm i mini-css-extract-plugin -D
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = (env, argv) => { return { ... plugins: [ ... new MiniCssExtractPlugin({ filename: "css/[name].[hash].css", chunkFilename: "css/[id].[hash].css", }) ], module: { rules: [ { test: /\.css/, use: [ MiniCssExtractPlugin.loader, 'css-loader' ] } ] } } }
|
我们npm run build,就发现css已经分离出,并且在html-webpack-plugin的帮助下,也将css导入到html中:
1 2 3 4 5 6
| ├── dist │ ├── index.html │ ├── css │ │ └── main.70c0912768e4fa3bf241.css │ └── js │ └── bundle.js
|
导入文件
我们开发中经常会使用某些静态文件,当然webpack是不支持这类文件的,比如png、svg这类文件,这个时候我们就需要使用file-loader来解析静态文件,配合url-loader可以将小文件转换成base64格式直接插入,减少请求次数.
- file-loader 处理各类文件
- url-loader 将指定大小的文件转换成base64
两者之间是什么关系呢,我们可以理解为url-loader是file-loader的拓展,但是url-loader必须要依靠file-loader的基础能力,所以我们需要将两个loader都下载:npm i file-loader url-loader -D
修改index.html:
1 2 3 4
| <body> <h2>this is a webpack test</h2> <div class="pic"></div> </body>
|
index.css中导入图片:
1 2 3 4 5
| .pic { height: 300px; width: 400px; background-image: url('./test.png') }
|
配置webpack.config.js:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| module.exports = (env, argv) => { return { ... module: { rules: [ ... { test: /\.(png|jpg|gif|jpeg|svg)$/, use: [ { loader: 'url-loader', options: { limit: 1024, name: '[name].[hash:6].[ext]', outputPath: 'images/', publicPath: '../images' } } ] } ] } } }
|
我们这里设置了limit,表示当文件小于1024B,即转换成base64格式,name设置了文件的name和hash值,outputPath则是文件存储的位置,publicPath则是引入文件时,路径前自带的初始化路径,以正确的索引文件.
执行npm run build,得到打包结果:
1 2 3 4 5 6 7 8
| ├── dist │ ├── index.html │ ├── css │ │ └── main.32e8e4a338b99c0f881f.css │ ├── images │ │ └── test.82e44a.png │ └── js │ └── bundle.js
|
最后
最后我们来看看整个webpack.config.js的配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| const path = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin') const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = (env, argv) => { return { entry: './src/index.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'js/bundle.js' }, plugins: [ new HtmlWebpackPlugin({ template: './src/index.html', filename: 'index.html' }), new MiniCssExtractPlugin({ filename: "css/[name].[hash].css", chunkFilename: "css/[id].[hash].css", }) ], module: { rules: [ { test: /\.css/, use: [ MiniCssExtractPlugin.loader, 'css-loader' ] }, { test: /\.(png|jpg|gif|jpeg|svg)$/, use: [ { loader: 'url-loader', options: { limit: 1024, name: '[name].[hash:6].[ext]', outputPath: 'images/', publicPath: '../images' } } ] } ] } } }
|
当然,这仅仅只是完成了最基础的功能,距离真正能够方便我们开发的配置或许还有一些距离,但是一般来说,以上配置都是必须的,所以我们不必要着急,慢慢来,请关注我后续博客,将会在此基础上实现更加完善的功能。
本章节的代码已经上传到Github,传送门webpack-study,请自行切换到chapter-01分支。
He that thinks his business below him will always be above his business. :)